<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------

namespace app\helper;

/**
 * @author  苦瓜 <358359915@qq.com>
 */
class Redis
{
    private $client;
    public function __construct()
    {
        $this->client = cache()->store('redis')->handler();
    }
    // 创建索引
    public function create(string $index, array $data){
        $builder = new IndexBuilder();
        // 
        $query = $builder->setIndex($index)->setPrefixes([$index . ':']);
        foreach($data as $key => $val){
            switch($val){
                case 'number':
                    $query->addNumericField($key);
                    break;
                case 'tag':
                    $query->addTagField($key, ',');
                    break;
                case 'geo':
                    $query->addField((new GeoFieldOption())->setField($key));
                    break;
                default:
                    $query->addTextField($key);
                    break;
            }
        }
        $query->create($this->client);

    }
    // 删除索引
    public function drop(string $index){
        $idx = new Index($index, $this->client);
        if($idx->delete()){

        }
    }
    // 添加文档
    public function add(string $index, int $id, array $row = []){
        $idx = new Index($index, $this->client);
        return $idx->addDocumentFromArray($row, $index . ':' . $id);
    }
    // 批量添加文档
    public function batch(string $index, array $rows = []){
        $idx = new Index($index, $this->client);
        foreach($rows as $row){
            $row = array_map(fn($v) => is_array($v) ? json_encode($v) : $v, $row);
            $idx->addDocumentFromArray($row, $index . ':' . $row['id']);
        }
    }
    // 
    public function status(string $index){
        $idx = new Index($index, $this->client);
        return $idx->getInfo();
    }
    // 查询
    public function index(string $index){
        $this->search->setIndex($index);
        return $this;
    }
    /**
     */
    private function query($field, $sign, $value = null)
    {
        if($sign == '!='){
            $this->qs[] = '-@' . $field . ':[' . $value . ' ' . $value . ']';
        }elseif($sign == '='){
            $this->qs[] = '@' . $field . ':[' . $value . ' ' . $value . ']';
        }elseif($sign == 'in'){
            !empty($value) && ($this->qs[] = '@' . $field . ':(' . implode('|', $value) . ')');
        }elseif($sign == 'like'){
            $this->qs[] = '@' . $field . ':' . $value . '*';
        }elseif($sign == 'between'){
            $this->qs[] = '@' . $field . ':[' . $value[0] . ' ' . $value[1] . ']';
        }elseif($sign == '>'){
            $this->qs[] = '@' . $field . ':[(' . $value . ' +inf]';
        }elseif($sign == '<'){
            $this->qs[] = '@' . $field . ':[-inf (' . $value . ']';
        }elseif($sign == '>='){
            $this->qs[] = '@' . $field . ':[' . $value . ' +inf]';
        }elseif($sign == '<='){
            $this->qs[] = '@' . $field . ':[-inf ' . $value . ']';
        }elseif($sign == 'geo'){
            $this->qs[] = '@' . $field . ':[' . $value[0] . ' ' . $value[1] . ' ' . $value[2] . 'km]';
        }elseif($value === null){
            $this->qs[] = '(@' . $field . ':' . $sign . ')';
        }
        return $this;
    }
    /**
     * 查询条件
     *
     * @param [type] $field
     * @param string $sign
     * @param [type] $value
     * @return void
     */
    public function where(string | array $field, string $sign = '=', $value = null)
    {
        if(is_string($field)){
            $this->query($field, $sign, $value);
        } elseif (is_array($field)) {
            foreach($field as $row){
                if(isset($row[2])){
                    $this->query($row[0], $row[1], $row[2]);
                } else {
                    $this->query($row[0], $row[1]);
                }
            }
        }
        return $this;
    }
    /**
     * 限制条数
     *
     * @param integer $size 分页到校
     * @param integer $offset 
     * @return this
     */
    public function limit(int $size = 10, int $offset = 0){
        $this->search->setLimit($offset, $size);
        return $this;
    }
    /**
     * 分页
     *
     * @param [type] $page
     * @param integer $size
     * @return void
     */
    public function page($page, $size = 10){
        $this->search->setLimit($size * ($page - 1), $size);
        return $this;
    }
    /**
     * 设置要读取的字段
     *
     * @param array $fields
     * @return void
     */
    public function field(array $fields = []){
        $this->search->setReturn(...$fields);
        return $this;
    }
    /**
     * 排序
     *
     * @param string $order
     * @param string|null $direction
     * @return void
     */
    public function order(string $order, ?string $direction = null){
        $this->search->setSortBy($order, $direction);
        return $this;
    }
    /**
     */
    public function select(): array
    {
        $this->search->setQuery(implode(' ', $this->qs));
        $this->qs = [];
        return array_map(fn($r)=>$r->getFields(), $this->client->execute($this->search)->current());
    }
    /**
     */
    public function find()
    {
        $this->limit(1, 0)->search->setQuery(implode(' ', $this->qs));
        $this->qs = [];
        $rows = $this->client->execute($this->search)->current();
        return count($rows) > 0 ? $rows[0]->getFields() : null;
    }
    /**
     * @return array
     */
    public function column(array | string $field = [], string $key = null): array
    {
        $cols = is_string($field) ? [$field] : $field;
        $l = count($cols);
        if($l > 0){
            $f = $key && !in_array($key, $cols) ? array_merge($cols, [$key]) : $cols;
            $this->search->setReturn(...$f);
        }
        $this->search->setQuery(implode(' ', $this->qs))->setLimit(0, 1000);
        $this->qs = [];
        $rows = $this->client->execute($this->search)->current();
        $list = [];
        foreach($rows as $row){
            $row = $row->getFields();
            if($key!==null && isset($row[$key])){
                $list[$row[$key]] = is_string($field) ? $row[$field] : $row;
            } else {
                $list[] = is_string($field) ? $row[$field] : $row;
            }
        }
        return $list;
    }
    /**
     */
    public function value(string $field = 'id')
    {
        $this->limit(1, 0)->search->setQuery(implode(' ', $this->qs));
        $this->qs = [];
        $rows = $this->client->execute($this->search)->current();
        return count($rows) > 0 ? $rows[0]->getFields()[$field] : null;
    }
    // 统计数量
    public function count(){
        $this->limit(0, 0)->search->setQuery(implode(' ', $this->qs));
        $this->qs = [];
        return $this->client->execute($this->search)->getTotalCount();
    }
    // 添加管道项
    public function exec(){
        $this->pipeline->executePipeline();
    }
    // 搜索器
    public function search(){
        return $this->search;
    }
    // 搜索器
    public function aggregate(){
        return $this->aggregate;
    }
    // 管道
    public function pipeline(){
        $query = '@age:[(17 +inf] %john%';
        $search = new Search();
        $search->setIndex('people')
            ->setQuery($query);
        
        $stats = new Aggregate();
        $stats->setIndex('people')
            ->setQuery($query)
            ->addGroupBy(new GroupByOption([], [
                ReduceOption::average('age', 'avg'),
                ReduceOption::maximum('age', 'oldest')
            ]));
        
        $aggregate = new Aggregate();
        $aggregate->setIndex('people')
            ->setQuery($query)
            ->addGroupBy(new GroupByOption(['lastname'], [ReduceOption::count('count')]));
        
        $suggestion = new SugGet();
        $suggestion->setDictionary('names')
            ->setPrefix('john')
            ->setFuzzy();
        
        $result = $this->client->pipeline($search, $stats, $aggregate, $suggestion);
        
        // $result[0] is the search result
        // $result[1] is the first aggregation result
        // $result[2] is the second aggregation result
        // $result[3] is the suggestion result
    }
}
